// basic actor

#pragma once

#include "facing.h"
#include "../tileManager.h"
#include "../timedCallback.h"
#include "../boundsCheck.h"

// all actors derive from this class
class Actor {
private:
	int absi(int i) {
		return (i < 0) ? -i : i;
	}

public:
	// position and orientation
	int x;			// current
	int y;			// current
	Facing o;		// current

	// home location, used when an enemie returns home
	int homeX;
 	int homeY;
	Facing homeO;

	bool alive;		// whether the actor is alive (active) or not
	bool mirror;	// whether the actor looks like the fake illusionist

	bool approachFlag;			// in approach mode
	bool fleeFlag;				// in flee mode
	bool returnOrigLocFlag;		// returning to original location

	// callback for this object
	TimedCallback *callback;

	Actor(int in_x, int in_y, Facing in_o)
		: x(in_x), y(in_y), o(in_o),
		  homeX(in_x), homeY(in_y), homeO(in_o),
		  alive(true), mirror(false),
		  approachFlag(false), fleeFlag(false), returnOrigLocFlag(false),
		  callback(NULL) {}

	//--- LIVENESS ---//
	bool isDead() {
		return !alive;
	}

	void kill() {
		alive = false;
	}

	//--- FAKE ILL ---//
	bool isFakeIll() {
		return mirror;
	}

	void makeFakeIll() {
		mirror = true;
		// TODO: set callback so person turns back into normal version
	}

	//--- MOVEMENT ---//

	// move in the direction of facing
	void approach() {
		int dx, dy;
		getDxAndDy(o, dx, dy);

		int nx = x + dx;
		int ny = y + dy;

		if (BoundsCheck::inBounds(nx, ny) && BoundsCheck::isFloor(nx, ny)) {
			x = nx;
			y = ny;
		}
	}

	// move in the opposite direction of facing
	void flee() {
		int dx, dy;

		// keep running in the opposite direction
		o = flip(homeO);
		getDxAndDy(o, dx, dy);

		int nx = x + dx;
		int ny = y + dy;

		if (BoundsCheck::inBounds(nx, ny) && BoundsCheck::isFloor(nx, ny)) {
			x = nx;
			y = ny;

		}
		else
		{
			// can go no further, stop running
			o = homeO;
			callback->kill();
			fleeFlag = false;
		}
	}

	// move back to the original location
	void returnOrigLoc() {
		if (atOrigLoc()) {
			return;
		}

		if (x < homeX) {
			// go east
			x++;
			o = FA_E;
		} else if (x > homeX) {
			// go west
			x--;
			o = FA_W;
		} else if (y < homeY) {
			// go south
			y++;
			o = FA_S;
		} else if (y > homeY) {
			// go north
			y--;
			o = FA_N;
		} else {
			// x and y location match, fix orientation
			o = homeO;
		}

		if (atOrigLoc()) {
			// this callback is done, actor in orig loc
			callback->kill();
			returnOrigLocFlag = false;
		}
	}

	//--- LOCATION ---//

	// returns true if the actor is at (in_x, in_y)
	bool isAt(int in_x, int in_y) {
		return x == in_x && y == in_y;
	}

	// returns the Manhattan distance of this actor to (in_x, in_y)
	int getManhattanDist(int in_x, int in_y) {
		return absi(in_x - x) + absi(in_y - y);
	}

	// returns true if at original location
	bool atOrigLoc() {
		return x == homeX && y == homeY && o == homeO;
	}

	//--- MOVEMENT MODES ---//
	bool inManipulateMode() {
		return approachFlag || fleeFlag;
	}

	bool inReturnOrigLocMode() {
		return returnOrigLocFlag;
	}

	//--- CALLBACKS ---//
	void addCallback(TimedCallback *cb) {
		// TODO: make sure this never triggers
#if 0
		if (callback != NULL) {
			printf("warning: clobbering existing callback\n");
		}
#endif
		callback = cb;
	}

	void invokeCallback() {
		if (callback != NULL) {
			callback->poll(this);
			removeCallbackIfDead();
		}
	}

	// also calls delete on the callback
	void removeCallbackIfDead() {
		if (callback->isDead()) {
			delete callback;
			callback = NULL;
		}
	}
};


static
void approachCallback(Actor *a) {
	a->approach();
	a->approachFlag = false;
}

static
void fleeCallback(Actor *a) {
	a->flee();
	//a->fleeFlag = false;
}

static
void returnOrigLocCallback(Actor *a) {
	a->returnOrigLoc();
	// not disabling because this is a repeating callback
}

// end of actor.h
